module framework.GLSLRenderer;
import std.string;
import derelict.sdl.sdl;
import derelict.opengl.gl;
import derelict.opengl.glu;
//import derelict.opengl.glext;
import shared.glExtLoader;

import framework.JobHub;
import std.string;
import std.conv;

//alias derelict.opengl.extension.arb.shader_objects.GLhandleARB GLhandleARB;
//alias derelict.opengl.extension.arb.vertex_buffer_object.GLenum GLenum;

Renderer _renderer;
GLhandleARB programObject;
GLint boneMatricesParam = -1;
GLint colorParam = -1;
GLint lightPosParam = -1;

GLint positionAttrib = -1;
GLint normalAttrib = -1;
GLint weightAttrib = -1;
GLint indexAttrib = -1;
GLint numBonesAttrib = -1;

static this(){
	_renderer=new Renderer();
}

Renderer renderer(){
	return _renderer;
}


class Renderer{
	
	private {
		const int   xResolution     = 800;
		const int   yResolution     = 600;
		
		const int   bitsPerPixel    = 24;
		
		const float fov             = 90.f;
		
		const float nearPlane       = .1f;
		
		const float farPlane        = 100.f;
		

		
		float lightPos[4] ;
		float color[4] ;
		
		
		/**
		  Setup some basic OpenGL parameters
		*/
		void setupGL() {
			glMatrixMode(GL_PROJECTION);
			glLoadIdentity();
			gluPerspective(fov, cast(float)xResolution / yResolution, nearPlane, farPlane);
			glMatrixMode(GL_MODELVIEW);
			glLoadIdentity();
		}
		
		void addShader(GLhandleARB programObject, char[] shaderSource, uint shaderType){
			GLhandleARB object = glCreateShaderObjectARB(shaderType);
		
			GLint length = shaderSource.length;
			char *cShaderSource=toStringz(shaderSource);
			glShaderSourceARB(object, 1, &cShaderSource, &length);
			writefln("-----------------------");
			glCompileShaderARB(object);
			writefln("-----------------------");
			GLint compiled = 0;
			glGetObjectParameterivARB(object, GL_OBJECT_COMPILE_STATUS_ARB, &compiled);
			writefln("-----------------------");
			assert(compiled);
			writefln("-----------------------");
			glAttachObjectARB(programObject, object);
		
			glDeleteObjectARB(object);
		
		}
		
		void setupShaders(){
			programObject = glCreateProgramObjectARB();
			
			addShader(programObject, shaderData, GL_VERTEX_SHADER_ARB);
			writefln("****");
			glBindAttribLocationARB(programObject, 0, "position");

			glLinkProgramARB(programObject);

			GLint linked = false;
			glGetObjectParameterivARB(programObject, GL_OBJECT_LINK_STATUS_ARB, &linked);
			assert(linked);
			writefln("****");
			glValidateProgramARB(programObject);

			GLint validated = false;
			glGetObjectParameterivARB(programObject, GL_OBJECT_VALIDATE_STATUS_ARB, &validated);
			assert(validated);
			writefln("****");
			positionAttrib = glGetAttribLocationARB(programObject, "position");
			assert(positionAttrib >= 0);

			writefln("****");
			normalAttrib = glGetAttribLocationARB(programObject, "normal");
			assert(normalAttrib >= 0);
			writefln("****");
			weightAttrib = glGetAttribLocationARB(programObject, "weight");
			assert(weightAttrib >= 0);
			writefln("****");
			indexAttrib = glGetAttribLocationARB(programObject, "index");
			assert(indexAttrib >= 0);
			
			numBonesAttrib = glGetAttribLocationARB(programObject, "numBones");
			assert(numBonesAttrib >= 0);

			colorParam = glGetUniformLocationARB(programObject, "color");
			assert(colorParam >= 0);

			lightPosParam = glGetUniformLocationARB(programObject, "lightPos");
			assert(lightPosParam >= 0);
		
			boneMatricesParam = glGetUniformLocationARB(programObject, "boneMatrices[0]");
			assert(boneMatricesParam >= 0);
			
			glUseProgramObjectARB(programObject);

			lightPos[0]=1.f;
			lightPos[1]=0.f;
			lightPos[2]=0.f;
			lightPos[3]=1.f;
			
			glUniform4fvARB(lightPosParam, 1, lightPos);
			
			color[0]=1.f;
			color[1]=1.f;
			color[2]=1.f;
			color[3]=1.f;
			
			glUniform4fvARB(colorParam, 1,  color); 
		
			glUseProgramObjectARB(0);
		}
		
		
		
		void init() {
			// initialize SDL, GL and GLU Derelict modules
			DerelictSDL.load();
			DerelictGL.load();
			DerelictGLU.load();
			
		
			// initialize SDL's VIDEO module
			SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
		
			// enable double-buffering
			SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
		
			// create our OpenGL window
			SDL_SetVideoMode(xResolution, yResolution, bitsPerPixel, SDL_OPENGL);
			SDL_WM_SetCaption(toStringz("My SDL Window"), null);
			loadAllOpenGLExtensions();
			//writefln("-----------------------");
			setupShaders();
			
			setupGL();
			
		}
		
		
		// be nice and release all resources
		void cleanup() {
			// tell SDL to quit
			SDL_Quit();
		
			// release GL, GLU and SDL's shared libs
			DerelictGLU.unload();
			DerelictGL.unload();
			DerelictSDL.unload();
		}
	}

	
	this(){
		init();
		jobHub.register(&this.update);
	}
	
	~this(){
		cleanup();
	}
	
	private void delegate()[] dels;
	
	void register(void delegate() del){
		dels ~=del;
	}
	
	
	void update(){
		
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
		glClearColor ( 0.2, 0.2, 0.2 , 0);
		glLoadIdentity();
		glUseProgramObjectARB(programObject);
		foreach(del;dels){
			del();
		}
		glUseProgramObjectARB(0);
		SDL_GL_SwapBuffers();
	}
	
	char[] shaderData = 
	
	/+"
		attribute vec4 position;
		void main(){
			gl_Position= gl_ModelViewProjectionMatrix * position;
			gl_FrontColor= vec4(1.0);
		}
	";+/
	
	"
		attribute vec4 position;
		attribute vec3 normal;
		attribute vec4 weight;
		attribute vec4 index;
		attribute float numBones;
		
		uniform mat4 boneMatrices[60];
		uniform vec4 color;
		uniform vec4 lightPos;
		
		void main()
		{
			vec4 transformedPosition = vec4(0.0);
			vec3 transformedNormal = vec3(0.0);
		
			vec4 curIndex = index;
			vec4 curWeight = weight;

			for (int i = 0; i < int(numBones); i++)
			{
				mat4 m44 = boneMatrices[int(curIndex.x)];
				transformedPosition += m44 * position * curWeight.x; 
		
				mat3 m33 = mat3(m44[0].xyz,
								m44[1].xyz,
								m44[2].xyz);
		
				transformedNormal += m33 * normal * curWeight.x;
		
				curIndex = curIndex.yzwx;
				curWeight = curWeight.yzwx;
			}
		
			gl_Position = gl_ModelViewProjectionMatrix * transformedPosition;
		
			transformedNormal = normalize(transformedNormal);
			gl_FrontColor = dot(transformedNormal, lightPos.xyz) * color;
		}
	";
}

import std.stdio;